#ifndef _CU_IMAGE_H_
#define _CU_IMAGE_H_

#include <cuda_runtime.h>

#include "cuTypes.h"

#pragma pack(push)
#pragma pack(1)

//////////////////////////////////////////////////////////////////////////
/// BMPHeader
/// This data structure holds header for BMP files
//////////////////////////////////////////////////////////////////////////

typedef struct BMPHEARDER
{
  int16     type;
  int32     size;
  int16     reserved1;
  int16     reserved2;
  int32     offset;
} BMPHeader;

//////////////////////////////////////////////////////////////////////////
/// BMPInfoHeader
/// This data structure holds information about the BMP file
/// This information includes width and height of an image and bpp
//////////////////////////////////////////////////////////////////////////

typedef struct BMPINFOHEADER
{
  int32     size;
  int32     width;
  int32     height;
  int16     planes;
  int16     bitsPerPixel;
  uint32    compression;
  uint32    imageSize;
  int32     xPelsPerMeter;
  int32     yPelsPerMeter;
  int32     clrUsed;
  int32     clrImportant;
} BMPInfoHeader;

#pragma pack(pop)

//////////////////////////////////////////////////////////////////////////
/// cuImage memory flags
/// CU_IMAGE_D_MEMORY  - pitch linear memory
/// CU_IMAGE_A_MEMORY - cuda array memory
//////////////////////////////////////////////////////////////////////////
#define CU_IMAGE_NONE              0x0000
#define CU_IMAGE_H_MEMORY          0x0001  // Host   memory
#define CU_IMAGE_D_MEMORY          0x0002  // Device memory
#define CU_IMAGE_A_MEMORY          0x0004  // Array  memory

//////////////////////////////////////////////////////////////////////////
/// cuImage graphics resource flags
/// CU_IMAGE_OGL_RESOURCE  - allocate OpenGL  resources PBO and Texture 
/// CU_IMAGE_DX9_RESOURCE  - allocate DirectX9  resource: Texture
/// CU_IMAGE_DX10_RESOURCE - allocate DirectX10 resource: Texture
//////////////////////////////////////////////////////////////////////////

#define CU_IMAGE_OGL_RESOURCE      0x0008
#define CU_IMAGE_DX9_RESOURCE      0x000F
#define CU_IMAGE_DX10_RESOURCE     0x0010


//////////////////////////////////////////////////////////////////////////
/// cuImage execution flags
/// CU_IMAGE_EXEC_HOST         - use a single host thread
/// CU_IMAGE_EXEC_CUDA         - use CUDA
/// CU_IMAGE_EXEC_HOST_SSE     - use a single host thread with SSE 
/// CU_IMAGE_EXEC_HOST_OMP     - use OMP
/// CU_IMAGE_EXEC_HOST_OMP_SSE - use OMP with SSE
//////////////////////////////////////////////////////////////////////////

#define CU_IMAGE_EXEC_HOST         0x1 
#define CU_IMAGE_EXEC_CUDA         0x2
#define CU_IMAGE_EXEC_HOST_SSE     0x4
#define CU_IMAGE_EXEC_HOST_OMP     0x8
#define CU_IMAGE_EXEC_HOST_OMP_SSE 0xF

//////////////////////////////////////////////////////////////////////////
// cuImage class
//////////////////////////////////////////////////////////////////////////
class cuImage_RGBA8
{
protected:
  uint32 w, h, p;

  uint32 gl_tex, gl_pbo;

  cudaArray * m_pA;
  uchar4    * m_pH;
  uchar4    * m_pD;

  //////////////////////////////////////////////////////////////////////////
  /// cuImage Null function to set all variables to zero
  //////////////////////////////////////////////////////////////////////////

  void Null();

  //////////////////////////////////////////////////////////////////////////
  /// cuImage setSize function sets w & h and computes pitch value
  //////////////////////////////////////////////////////////////////////////

  void setSize(uint32 w, uint32 h);

  //////////////////////////////////////////////////////////////////////////
  /// cuImage protected resource management
  //////////////////////////////////////////////////////////////////////////
  
  //////////////////////////////////////////////////////////////////////////
  /// cuImage release Host memory
  //////////////////////////////////////////////////////////////////////////

  bool Release_Host();

  //////////////////////////////////////////////////////////////////////////
  /// cuImage release CUDA memory (linear and array)
  //////////////////////////////////////////////////////////////////////////

  bool Release_CUDA(uint8 flags = CU_IMAGE_D_MEMORY);

  //////////////////////////////////////////////////////////////////////////
  /// cuImage release OpenGL resources (texture and PBO)
  //////////////////////////////////////////////////////////////////////////

  bool Release_OGL();

  //////////////////////////////////////////////////////////////////////////
  /// cuImage allocate Host memory
  //////////////////////////////////////////////////////////////////////////

  bool Create_Host();

  //////////////////////////////////////////////////////////////////////////
  /// cuImage allocate CUDA memory (linear and array)
  //////////////////////////////////////////////////////////////////////////

  bool Create_CUDA(uint8 flags = CU_IMAGE_D_MEMORY, bool bCopy = true);

  //////////////////////////////////////////////////////////////////////////
  /// cuImage allocate OpenGL resources (texture and PBO)
  //////////////////////////////////////////////////////////////////////////

  bool Create_OGL();

public:

  cuImage_RGBA8();
  cuImage_RGBA8(const cuImage_RGBA8 & image);
  ~cuImage_RGBA8();

  cuImage_RGBA8 operator = (const cuImage_RGBA8 & image);

  //////////////////////////////////////////////////////////////////////////
  // cuImage "dynamic" filters: resource creation happens inside the call
  //////////////////////////////////////////////////////////////////////////

  bool LoadBMP(const char * pFileName, uint8 cFlags);
  bool SaveBMP(const char * pFileName);

  //////////////////////////////////////////////////////////////////////////
  // cuImage resource management
  //////////////////////////////////////////////////////////////////////////

  bool Release(uint8 dFlags = 0xFF);
  bool Create(uint32 width, 
              uint32 height, 
              uint8  cFlags);

  //////////////////////////////////////////////////////////////////////////
  // cuImage "dynamic" filters: resource creation happens inside the call
  //////////////////////////////////////////////////////////////////////////
  cuImage_RGBA8 * Clone();
  cuImage_RGBA8 * Gaussian(float sigma, 
                           uint8 cFlags, 
                           uint8 rFlags, 
                           uint8 wFlags);

  //////////////////////////////////////////////////////////////////////////
  // cuImage "static" filters: resource creation happens before the call
  //////////////////////////////////////////////////////////////////////////
  static bool Clone   (cuImage_RGBA8 * pDst, 
                       cuImage_RGBA8 * pSrc);

  static bool Gaussian(cuImage_RGBA8 * pDst, 
                       cuImage_RGBA8 * pSrc, 
                       float sigma,
                       uint8 rFlags,
                       uint8 wFlags);

public:

  //////////////////////////////////////////////////////////////////////////
  // cuImage "get*" functions
  //////////////////////////////////////////////////////////////////////////

  uchar4    * getHPtr()  { return m_pH;  }
  uchar4    * getDPtr()  { return m_pD;  }
  cudaArray * getAPtr()  { return m_pA;  } 

  int32       getW()     { return w;     }
  int32       getH()     { return h;     }
  int32       getP()     { return p;     }
  
  uint32      getGlPBO() { return gl_pbo; } 
  uint32      getGlTex() { return gl_tex; } 

public:

  //////////////////////////////////////////////////////////////////////////
  // cuImage CUDA device and device init functions
  //////////////////////////////////////////////////////////////////////////
  static cudaDeviceProp device_properties;
  static void InitDeviceProperties();

};

#endif